use crate::{
    CodeChange, EdgeId, EdgeMetadata, EdgeType, GraphUpdateResult, NodeId, NodeMetadata, NodeType,
    QueryResult, Result, SourceLocation, TextEdit,
};
use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;

/// Core trait for syntax tree representation
pub trait SyntaxTree: Send + Sync {
    /// Get the root node of the tree
    fn root_node(&self) -> Box<dyn SyntaxNode>;

    /// Execute a query on the tree
    fn query(&self, pattern: &str) -> Result<Vec<QueryMatch>>;

    /// Get the source code that this tree represents
    fn source(&self) -> &str;

    /// Get the language of this tree
    fn language(&self) -> &str;

    /// Downcast to concrete type (for internal use)
    fn as_any(&self) -> &dyn std::any::Any;
}

/// Trait for syntax tree nodes
pub trait SyntaxNode: Send + Sync + std::fmt::Debug {
    /// Get the node type
    fn kind(&self) -> &str;
    
    /// Get the text content of this node
    fn text(&self) -> &str;
    
    /// Get the start byte offset
    fn start_byte(&self) -> u32;
    
    /// Get the end byte offset
    fn end_byte(&self) -> u32;
    
    /// Get the start position
    fn start_position(&self) -> crate::Position;
    
    /// Get the end position
    fn end_position(&self) -> crate::Position;
    
    /// Get child nodes
    fn children(&self) -> Vec<Box<dyn SyntaxNode>>;
    
    /// Get a specific child by index
    fn child(&self, index: usize) -> Option<Box<dyn SyntaxNode>>;
    
    /// Get the number of children
    fn child_count(&self) -> usize;
}

/// Query match result
#[derive(Debug)]
pub struct QueryMatch {
    pub pattern_index: u32,
    pub captures: HashMap<String, Box<dyn SyntaxNode>>,
}

/// Core parser trait for different languages
#[async_trait]
pub trait Parser: Send + Sync {
    /// Get the language identifier
    fn language_id(&self) -> &str;
    
    /// Get the parser version
    fn version(&self) -> &str;
    
    /// Parse source code into a syntax tree
    async fn parse(&self, source: &str, old_tree: Option<&dyn SyntaxTree>) -> Result<Box<dyn SyntaxTree>>;
    
    /// Parse incrementally with text edits
    async fn parse_incremental(
        &self,
        edit: &TextEdit,
        old_tree: &dyn SyntaxTree,
    ) -> Result<Box<dyn SyntaxTree>>;
    
    /// Execute a query on a syntax tree
    fn query(&self, pattern: &str, tree: &dyn SyntaxTree) -> Result<Vec<QueryMatch>>;
    
    /// Clean up resources
    async fn dispose(&self) -> Result<()>;
}

/// Trait for code graph nodes
pub trait CodeNode: Send + Sync {
    /// Get the unique identifier
    fn id(&self) -> NodeId;
    
    /// Get the node type
    fn node_type(&self) -> &NodeType;
    
    /// Get the node name
    fn name(&self) -> &str;
    
    /// Get the source location
    fn location(&self) -> &SourceLocation;
    
    /// Get the programming language
    fn language(&self) -> &str;
    
    /// Get the metadata
    fn metadata(&self) -> &NodeMetadata;
    
    /// Get edges of a specific type
    fn edges(&self, edge_type: Option<&EdgeType>) -> Vec<&dyn CodeEdge>;
    
    /// Add an edge to this node
    fn add_edge(&mut self, edge: Box<dyn CodeEdge>) -> Result<()>;
    
    /// Remove an edge by ID
    fn remove_edge(&mut self, edge_id: EdgeId) -> Result<bool>;
    
    /// Update metadata
    fn update_metadata(&mut self, metadata: NodeMetadata) -> Result<()>;

    /// Downcast to concrete type (for internal use)
    fn as_any(&self) -> &dyn std::any::Any;

    /// Downcast to concrete type (for internal use, mutable)
    fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
}

/// Trait for code graph edges
pub trait CodeEdge: Send + Sync {
    /// Get the unique identifier
    fn id(&self) -> EdgeId;
    
    /// Get the source node ID
    fn source(&self) -> NodeId;
    
    /// Get the target node ID
    fn target(&self) -> NodeId;
    
    /// Get the edge type
    fn edge_type(&self) -> &EdgeType;
    
    /// Get the edge metadata
    fn metadata(&self) -> &EdgeMetadata;
    
    /// Update metadata
    fn update_metadata(&mut self, metadata: EdgeMetadata) -> Result<()>;
}

/// Trait for graph storage operations
#[async_trait]
pub trait GraphStorage: Send + Sync {
    /// Save a node to storage
    async fn save_node(&self, node: &dyn CodeNode) -> Result<()>;
    
    /// Get a node by ID
    async fn get_node(&self, id: NodeId) -> Result<Option<Box<dyn CodeNode>>>;
    
    /// Delete a node by ID
    async fn delete_node(&self, id: NodeId) -> Result<bool>;
    
    /// Save an edge to storage
    async fn save_edge(&self, edge: &dyn CodeEdge) -> Result<()>;
    
    /// Get edges for a node
    async fn get_edges(&self, node_id: NodeId, edge_type: Option<&EdgeType>) -> Result<Vec<Box<dyn CodeEdge>>>;
    
    /// Delete an edge by ID
    async fn delete_edge(&self, id: EdgeId) -> Result<bool>;
    
    /// Save multiple nodes and edges in a batch
    async fn save_batch(
        &self,
        nodes: &[&dyn CodeNode],
        edges: &[&dyn CodeEdge],
    ) -> Result<()>;
    
    /// Execute a query on the graph
    async fn query(&self, query: &GraphQuery) -> Result<QueryResult>;
    
    /// Begin a transaction
    async fn begin_transaction(&self) -> Result<Box<dyn Transaction>>;
}

/// Graph query interface
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphQuery {
    pub node_types: Option<Vec<NodeType>>,
    pub edge_types: Option<Vec<EdgeType>>,
    pub languages: Option<Vec<String>>,
    pub name_pattern: Option<String>,
    pub location_filter: Option<LocationFilter>,
    pub metadata_filter: Option<HashMap<String, serde_json::Value>>,
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

/// Location filter for queries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct LocationFilter {
    pub file_path: Option<String>,
    pub line_range: Option<(u32, u32)>,
    pub column_range: Option<(u32, u32)>,
}

/// Transaction trait for atomic operations
#[async_trait]
pub trait Transaction: Send + Sync {
    /// Commit the transaction
    async fn commit(self: Box<Self>) -> Result<()>;
    
    /// Rollback the transaction
    async fn rollback(self: Box<Self>) -> Result<()>;
    
    /// Save a node within the transaction
    async fn save_node(&self, node: &dyn CodeNode) -> Result<()>;
    
    /// Save an edge within the transaction
    async fn save_edge(&self, edge: &dyn CodeEdge) -> Result<()>;
    
    /// Delete a node within the transaction
    async fn delete_node(&self, id: NodeId) -> Result<bool>;
    
    /// Delete an edge within the transaction
    async fn delete_edge(&self, id: EdgeId) -> Result<bool>;
}

/// Language adapter trait for multi-language support
#[async_trait]
pub trait LanguageAdapter: Send + Sync {
    /// Get the language identifier
    fn language_id(&self) -> &str;
    
    /// Get supported file extensions
    fn file_extensions(&self) -> &[String];
    
    /// Create a parser for this language
    async fn create_parser(&self) -> Result<Box<dyn Parser>>;
    
    /// Extract nodes from a syntax tree
    async fn extract_nodes(&self, tree: &dyn SyntaxTree) -> Result<Vec<Box<dyn CodeNode>>>;
    
    /// Extract edges from a syntax tree
    async fn extract_edges(&self, tree: &dyn SyntaxTree, nodes: &[&dyn CodeNode]) -> Result<Vec<Box<dyn CodeEdge>>>;
    
    /// Normalize a node name
    fn normalize_node_name(&self, name: &str) -> String {
        name.trim().to_string()
    }
    
    /// Map syntax node type to graph node type
    fn map_syntax_type_to_node_type(&self, syntax_type: &str) -> NodeType;
}

/// Incremental update engine trait
#[async_trait]
pub trait IncrementalEngine: Send + Sync {
    /// Update the graph based on code changes
    async fn update_graph(&self, change: &CodeChange) -> Result<GraphUpdateResult>;
    
    /// Compute differences between syntax trees
    async fn compute_node_differences(
        &self,
        old_tree: &dyn SyntaxTree,
        new_tree: &dyn SyntaxTree,
    ) -> Result<crate::NodeDifference>;
    
    /// Propagate changes through the graph
    async fn propagate_changes(&self, changes: &crate::NodeDifference) -> Result<Vec<NodeId>>;
}
